package org.moonlight.jvm.instructions.references.invoke;

import org.apache.commons.lang3.ObjectUtils;
import org.moonlight.jvm.instructions.base.MethodInvokeLogic;
import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.share.Object;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.classmember.Method;
import org.moonlight.jvm.rtda.share.heap.symref.MethodRef;
import org.moonlight.jvm.rtda.share.methodarea.RtConstantPool;
import org.moonlight.jvm.utils.MethodLookupUtil;

/**
 * Invoke_Special: 调用一些需要特殊处理的实例方法，包括实例初始化方法、私有方法和父类方法
 *
 * @author Moonlight
 * @createTime 2023/8/30 16:47
 **/
public class InvokeSpecial extends MethodInvokeLogic {

    @Override
    public void execute(Frame frame) {
        // 当前类
        Class currentClass = frame.getMethod().getClazz();
        // 当前运行时常量池
        RtConstantPool rtCp = currentClass.getRtCp();
        // 要调用的方法的符号引用
        MethodRef methodRef = (MethodRef) rtCp.getConstant(this.idx);
        // 解析出来的类对象
        Class resolvedClass = methodRef.resolvedClass();
        // 解析出来的方法
        Method resolvedMethod = methodRef.resolvedMethod();

        // 假定从方法符号引用中解析出来的类是resolvedClass，方法是resolvedMethod。
        // 如果resolvedMethod是构造函数，则声明resolvedMethod的类必须是resolvedClass，否则抛出NoSuchMethodError异常。
        if ("<init>".equals(resolvedMethod.getName()) && ObjectUtils.notEqual(resolvedMethod.getClazz(), resolvedClass)) {
            throw new NoSuchMethodError();
        }
        // 如果M是静态方法，则抛出IncompatibleClassChangeError异常。
        if (resolvedMethod.isStatic()) {
            throw new IncompatibleClassChangeError();
        }
        // 从操作数栈弹出this引用
        Object ref = frame.getOperandStack().GetRefFromTop(resolvedMethod.getArgSlotCount() - 1);
        if (ref == null) {
            throw new NullPointerException();
        }
        // 如果方法被声明为 protected 那么就要确保这个方法只能被 声明类或者声明类的子类所调用
        if (resolvedMethod.isProtected()
            && resolvedMethod.getClazz().isSuperClassOf(currentClass)
            && ObjectUtils.notEqual(resolvedMethod.getClazz().getPackageName(), currentClass.getPackageName())
            && ObjectUtils.notEqual(ref.getClazz(), currentClass)
            && !ref.getClazz().isSubClassOf(currentClass)) {
            throw new IllegalAccessError();
        }
        Method methodToBeInvoked = resolvedMethod;
        // 如果调用的中超类中的函数，但不是构造函数，且当前类的ACC_SUPER标志被设置，需要一个额外的过程查找最终要调用的方法；
        // 否则前面从方法符号引用中解析出来的方法就是要调用的方法。
        if (currentClass.isSuper() && resolvedClass.isSuperClassOf(currentClass) && !"<init>".equals(resolvedMethod.getName())) {
            methodToBeInvoked = MethodLookupUtil.lookupMethodInClass(currentClass.getSuperClass(), methodRef.getName(), methodRef.getDescriptor());
        }
        if (methodToBeInvoked == null || methodToBeInvoked.isAbstract()) {
            throw new AbstractMethodError();
        }
        invokeMethod(frame, methodToBeInvoked);
    }

}
